﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace Client
{
    public partial class Form1 : Form
    {
        
        private readonly WebCam _wc;

        public Form1()
        {
            InitializeComponent();
            _wc = new WebCam(panel1.Handle, 0, 0, panel1.Width, panel1.Height);
            
            btnDisp.Text = "Live view";
            btnDisp.Click +=
                (s, e) =>
                    {
                        if (btnDisp.Text == "Live view")
                        {
                            _wc.Width = panel1.Width;
                            _wc.Height = panel1.Height;
                            _wc.Start();
                            btnDisp.Text = "Stop Live view";
                            return;
                        }
                        _wc.Stop();
                        btnDisp.Text = "Live view";
                    };
            btnSnapshot.Click +=
                (s, e) =>
                    {
                        using (var fn = new SaveFileDialog())
                        {
                            if (fn.ShowDialog() != System.Windows.Forms.DialogResult.OK) return;
                            _wc.Snapshot(fn.FileName);
                        }
                    };
            btnRecord.Text = "Start Record";
            btnRecord.Click +=
                (s, e) =>
                    {
                        if (btnRecord.Text == "Start Record")
                        {
                            using (var fn = new SaveFileDialog())
                            {
                                if (fn.ShowDialog() != DialogResult.OK) return;
                                _wc.Record(fn.FileName);
                                btnRecord.Text = "Stop Record";
                                return;
                            }
                        }
                        _wc.StopRecord();
                        btnRecord.Text = "Start Record";
                    };
            panel1.Paint +=
                (s, e) =>
                    {
                        e.Graphics.DrawString("ASDF", panel1.Font, Brushes.White, 10, 10);
                    };
        }

        private bool _flag;
        private Foo _foo;
        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            if (_flag)
            {
                _foo.Close();
                _flag = false;
                return;
            }
            _flag = true;
            _foo = new Foo();
            _foo.VideoBufferReceived +=
                (s, a) =>
                    {
                        //Console.WriteLine(a.Length);
                    };
            _foo.BitmapReceived +=
                (s, a) =>
                    {
                        //Console.WriteLine(a.Data.Height);
                        panel2.BackgroundImage = a.Data;
                    };
            _foo.Start(0, panel1.Handle, panel1.Width, panel1.Height);

        }
    }

    public class WebCam
    {
        private const int WM_USER                           = 0x400;
        private const int WS_CHILD                          = 0x40000000;
        private const int WS_VISIBLE                        = 0x10000000;
        private const int WM_CAP_START                      = WM_USER;
        private const int WM_CAP_SET_CALLBACK_ERROR         = WM_CAP_START + 2;
        private const int WM_CAP_SET_CALLBACK_STATUSA       = WM_CAP_START + 3;
        private const int WM_CAP_SET_CALLBACK_FRAME         = WM_CAP_START + 5;
        private const int WM_CAP_SET_CALLBACK_VIDEOSTREAM   = WM_CAP_START + 6;
        private const int WM_CAP_DRIVER_CONNECT             = WM_CAP_START + 10;
        private const int WM_CAP_DRIVER_DISCONNECT          = WM_CAP_START + 11;
        private const int WM_CAP_FILE_SET_CAPTURE_FILEA     = WM_CAP_START + 20;
        private const int WM_CAP_SAVEDIB                    = WM_CAP_START + 25;
        private const int WM_CAP_SET_PREVIEW                = WM_CAP_START + 50;
        private const int WM_CAP_SET_OVERLAY                = WM_CAP_START + 51;
        private const int WM_CAP_SET_PREVIEWRATE            = WM_CAP_START + 52;
        private const int WM_CAP_SET_SCALE                  = WM_CAP_START + 53;
        private const int WM_CAP_GRAB_FRAME                 = WM_CAP_START + 60;
        private const int WM_CAP_SEQUENCE                   = WM_CAP_START + 62;
        private const int WM_CAP_SEQUENCE_NOFILE            = WM_CAP_START + 63;
        private const int WM_CAP_STOP                       = WM_CAP_START + 68;


        public IntPtr Handle { get; private set; }
        public int X { get; set; }
        public int Y { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }

        private readonly List<object> _isRunning = new List<object>();
        public bool IsRunning { get { return _isRunning.Count > 0; } }

        private IntPtr _hwnd;

        public WebCam(IntPtr handle, int x, int y, int width, int height)
        {
            Handle = handle;
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }

        [DllImport("avicap32.dll")]
        private static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nId);
        [DllImport("avicap32.dll")]
        private static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);
        [DllImport("User32.dll")]
        private static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, long lParam);

        public bool Start()
        {
            if (IsRunning) return false;
            _isRunning.Add(1);
            var name = new byte[256];
            _hwnd = capCreateCaptureWindowA(name, WS_CHILD | WS_VISIBLE, X, Y, Width, Height, Handle, 0);
            if (_hwnd == IntPtr.Zero)
            {
                _isRunning.Clear();
                return false;
            }
            SendMessage(_hwnd, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);
            SendMessage(_hwnd, WM_CAP_SET_CALLBACK_STATUSA, 0, 0);
            SendMessage(_hwnd, WM_CAP_DRIVER_CONNECT, 0, 0);
            SendMessage(_hwnd, WM_CAP_SET_SCALE, 1, 0);
            SendMessage(_hwnd, WM_CAP_SET_PREVIEWRATE, 66, 0);
            SendMessage(_hwnd, WM_CAP_SET_OVERLAY, 1, 0);
            SendMessage(_hwnd, WM_CAP_SET_PREVIEW, 1, 0);
            return true;
        }
        public void Stop()
        {
            if (!IsRunning) return;
            SendMessage(_hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
            _isRunning.Clear();
        }
        public void Snapshot(string path)
        {
            if (!IsRunning) throw new InvalidOperationException();
            var fn = Marshal.StringToHGlobalAnsi(path);
            SendMessage(_hwnd, WM_CAP_SAVEDIB, 0, fn.ToInt64());
        }
        public void Record(string path)
        {
            if (!IsRunning) throw new InvalidOperationException();
            var fn = Marshal.StringToHGlobalAnsi(path);
            SendMessage(_hwnd, WM_CAP_FILE_SET_CAPTURE_FILEA, 0, fn.ToInt64());
            SendMessage(_hwnd, WM_CAP_SEQUENCE, 0, 0);
        }
        public void StopRecord()
        {
            if (!IsRunning) throw new InvalidOperationException();
            SendMessage(_hwnd, WM_CAP_STOP, 0, 0);
        }
    }

    public static class W32
    {
        // showVideo calls
        [DllImport("avicap32.dll")]
        public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
        [DllImport("avicap32.dll")]
        public static extern bool capGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, FrameEventHandler lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);
        [DllImport("User32.dll")]
        public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
        [DllImport("avicap32.dll")]
        public static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);

        // Constants
        public const int WM_USER = 0x400;
        public const int WS_CHILD = 0x40000000;
        public const int WS_VISIBLE = 0x10000000;
        public const int SWP_NOMOVE = 0x2;
        public const int SWP_NOZORDER = 0x4;
        public const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
        public const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
        public const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
        public const int WM_CAP_SET_PREVIEW = WM_USER + 50;
        public const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
        public const int WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;

        // Structures
        [StructLayout(LayoutKind.Sequential)]
        public struct VIDEOHDR
        {
            [MarshalAs(UnmanagedType.I4)]
            public int lpData;
            [MarshalAs(UnmanagedType.I4)]
            public int dwBufferLength;
            [MarshalAs(UnmanagedType.I4)]
            public int dwBytesUsed;
            [MarshalAs(UnmanagedType.I4)]
            public int dwTimeCaptured;
            [MarshalAs(UnmanagedType.I4)]
            public int dwUser;
            [MarshalAs(UnmanagedType.I4)]
            public int dwFlags;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public int[] dwReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biSize;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biWidth;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biHeight;
            [MarshalAs(UnmanagedType.I2)]
            public short biPlanes;
            [MarshalAs(UnmanagedType.I2)]
            public short biBitCount;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biCompression;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biSizeImage;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biXPelsPerMeter;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biYPelsPerMeter;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biClrUsed;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biClrImportant;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            [MarshalAs(UnmanagedType.Struct, SizeConst = 40)]
            public BITMAPINFOHEADER bmiHeader;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
            public Int32[] bmiColors;
        }

        public delegate void FrameEventHandler(IntPtr lwnd, IntPtr lpVHdr);

        // Public methods
        public static object GetStructure(IntPtr ptr, ValueType structure)
        {
            return Marshal.PtrToStructure(ptr, structure.GetType());
        }

        public static object GetStructure(int ptr, ValueType structure)
        {
            return GetStructure(new IntPtr(ptr), structure);
        }

        public static void Copy(IntPtr ptr, byte[] data)
        {
            Marshal.Copy(ptr, data, 0, data.Length);
        }

        public static void Copy(int ptr, byte[] data)
        {
            Copy(new IntPtr(ptr), data);
        }

        public static int SizeOf(object structure)
        {
            return Marshal.SizeOf(structure);
        }
    }

    public static class VideoForWindows
    {

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct VideoHeaderTag
        {
            public IntPtr lpData;
            public uint dwBufferLength;
            public uint dwBytesUsed;
            public uint dwTimeCaptured;
            public uint dwUser;
            public uint dwFlags;

            /// DWORD_PTR[4]
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.U4)]
            public uint[] dwReserved;
        }


        [DllImportAttribute("<Unknown>", EntryPoint = "capVideoStreamCallback", CallingConvention = CallingConvention.StdCall)]
        [return: MarshalAsAttribute(UnmanagedType.SysInt)]
        public static extern int capVideoStreamCallback(IntPtr hWnd, ref VideoHeaderTag lpVHdr);

    }

    /// 
    /// avicap 的摘要说明。
    /// 
    public class showVideo
    {
        // showVideo calls
        [DllImport("avicap32.dll")]
        public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
        [DllImport("avicap32.dll")]
        public static extern bool capGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, FrameEventHandler lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);
        [DllImport("User32.dll")]
        public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
        [DllImport("avicap32.dll")]
        public static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);

        // Constants
        public const int WM_USER = 0x400;
        public const int WS_CHILD = 0x40000000;
        public const int WS_VISIBLE = 0x10000000;
        public const int SWP_NOMOVE = 0x2;
        public const int SWP_NOZORDER = 0x4;
        public const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
        public const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
        public const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
        public const int WM_CAP_SET_PREVIEW = WM_USER + 50;
        public const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
        public const int WM_CAP_SET_VIDEOFORMAT = WM_USER + 45;

        // Structures
        [StructLayout(LayoutKind.Sequential)]
        public struct VIDEOHDR
        {
            [MarshalAs(UnmanagedType.I4)]
            public int lpData;
            [MarshalAs(UnmanagedType.I4)]
            public int dwBufferLength;
            [MarshalAs(UnmanagedType.I4)]
            public int dwBytesUsed;
            [MarshalAs(UnmanagedType.I4)]
            public int dwTimeCaptured;
            [MarshalAs(UnmanagedType.I4)]
            public int dwUser;
            [MarshalAs(UnmanagedType.I4)]
            public int dwFlags;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public int[] dwReserved;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biSize;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biWidth;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biHeight;
            [MarshalAs(UnmanagedType.I2)]
            public short biPlanes;
            [MarshalAs(UnmanagedType.I2)]
            public short biBitCount;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biCompression;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biSizeImage;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biXPelsPerMeter;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biYPelsPerMeter;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biClrUsed;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biClrImportant;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            [MarshalAs(UnmanagedType.Struct, SizeConst = 40)]
            public BITMAPINFOHEADER bmiHeader;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
            public Int32[] bmiColors;
        }

        public delegate void FrameEventHandler(IntPtr lwnd, IntPtr lpVHdr);

        // Public methods
        public static object GetStructure(IntPtr ptr, ValueType structure)
        {
            return Marshal.PtrToStructure(ptr, structure.GetType());
        }

        public static T PointerToStructure<T>(IntPtr ptr)
        {
            return (T) Marshal.PtrToStructure(ptr, typeof (T));
        }

        public static object GetStructure(int ptr, ValueType structure)
        {
            return GetStructure(new IntPtr(ptr), structure);
        }

        public static void Copy(IntPtr ptr, byte[] data)
        {
            Marshal.Copy(ptr, data, 0, data.Length);
        }

        public static void Copy(int ptr, byte[] data)
        {
            Copy(new IntPtr(ptr), data);
        }

        public static int SizeOf(object structure)
        {
            return Marshal.SizeOf(structure);
        }
    }

    //Web Camera Class
    public class WebCamera
    {
        // Constructur
        public WebCamera(IntPtr handle, int width, int height)
        {
            mControlPtr = handle;
            mWidth = width;
            mHeight = height;
        }

        // delegate for frame callback
        public delegate void RecievedFrameEventHandler(byte[] data);
        public event RecievedFrameEventHandler RecievedFrame;

        private IntPtr lwndC; // Holds the unmanaged handle of the control
        private IntPtr mControlPtr; // Holds the managed pointer of the control
        private int mWidth;
        private int mHeight;

        private showVideo.FrameEventHandler mFrameEventHandler; // Delegate instance for the frame callback - must keep alive! gc should NOT collect it

        // Close the web camera
        public void CloseWebcam()
        {
            this.capDriverDisconnect(this.lwndC);
        }

        // start the web camera
        public void StartWebCam()
        {
            byte[] lpszName = new byte[100];
            byte[] lpszVer = new byte[100];

            showVideo.capGetDriverDescriptionA(0, lpszName, 100, lpszVer, 100);
            this.lwndC = showVideo.capCreateCaptureWindowA(lpszName, showVideo.WS_VISIBLE + showVideo.WS_CHILD, 0, 0, mWidth, mHeight, mControlPtr, 0);

            if (this.capDriverConnect(this.lwndC, 0))
            {
                this.capPreviewRate(this.lwndC, 66);
                this.capPreview(this.lwndC, true);
                showVideo.BITMAPINFO bitmapinfo = new showVideo.BITMAPINFO();
                bitmapinfo.bmiHeader.biSize = showVideo.SizeOf(bitmapinfo.bmiHeader);
                bitmapinfo.bmiHeader.biWidth = 352;
                bitmapinfo.bmiHeader.biHeight = 288;
                bitmapinfo.bmiHeader.biPlanes = 1;
                bitmapinfo.bmiHeader.biBitCount = 24;
                this.capSetVideoFormat(this.lwndC, ref bitmapinfo, showVideo.SizeOf(bitmapinfo));
                this.mFrameEventHandler = new showVideo.FrameEventHandler(FrameCallBack);
                this.capSetCallbackOnFrame(this.lwndC, this.mFrameEventHandler);
                showVideo.SetWindowPos(this.lwndC, 0, 0, 0, mWidth, mHeight, 6);
            }
        }

        // private functions
        private bool capDriverConnect(IntPtr lwnd, short i)
        {
            return showVideo.SendMessage(lwnd, showVideo.WM_CAP_DRIVER_CONNECT, i, 0);
        }

        private bool capDriverDisconnect(IntPtr lwnd)
        {
            return showVideo.SendMessage(lwnd, showVideo.WM_CAP_DRIVER_DISCONNECT, 0, 0);
        }

        private bool capPreview(IntPtr lwnd, bool f)
        {
            return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_PREVIEW, f, 0);
        }

        private bool capPreviewRate(IntPtr lwnd, short wMS)
        {
            return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_PREVIEWRATE, wMS, 0);
        }

        private bool capSetCallbackOnFrame(IntPtr lwnd, showVideo.FrameEventHandler lpProc)
        {
            return showVideo.SendMessage(lwnd, showVideo.WM_CAP_SET_CALLBACK_FRAME, 0, lpProc);
        }

        private bool capSetVideoFormat(IntPtr hCapWnd, ref showVideo.BITMAPINFO BmpFormat, int CapFormatSize)
        {
            return showVideo.SendMessage(hCapWnd, showVideo.WM_CAP_SET_VIDEOFORMAT, CapFormatSize, ref BmpFormat);
        }

        private void FrameCallBack(IntPtr lwnd, IntPtr lpVHdr)
        {
            showVideo.VIDEOHDR videoHeader = new showVideo.VIDEOHDR();
            byte[] VideoData;
            videoHeader = (showVideo.VIDEOHDR) showVideo.GetStructure(lpVHdr, videoHeader);
            VideoData = new byte[videoHeader.dwBytesUsed];
            showVideo.Copy(videoHeader.lpData, VideoData);
            if (this.RecievedFrame != null)
                this.RecievedFrame(VideoData);
        }
    }

    public class VideoBufferReceivedEventArgs : EventArgs
    {
        public byte[] Buffer { get; private set; }
        public int Length { get; private set; }
        public VideoBufferReceivedEventArgs(byte[] buffer, int length)
        {
            Buffer = buffer;
            Length = length;
        }
    }
    public class DataEventArgs<T> : EventArgs
    {
        public T Data { get; set; }
        public DataEventArgs(T data)
        {
            Data = data;
        }
    }

    public class Foo
    {
        #region const
        private const int WM_USER                           = 0x400;
        private const int WS_CHILD                          = 0x40000000;
        private const int WS_VISIBLE                        = 0x10000000;
        private const int WM_CAP_START                      = WM_USER;
        private const int WM_CAP_GET_CAPSTREAMPTR           = WM_CAP_START + 1;
        private const int WM_CAP_SET_CALLBACK_ERROR         = WM_CAP_START + 2;
        private const int WM_CAP_SET_CALLBACK_STATUSA       = WM_CAP_START + 3;
        private const int WM_CAP_SET_CALLBACK_YIELD         = WM_CAP_START + 4;
        private const int WM_CAP_SET_CALLBACK_FRAME         = WM_CAP_START + 5;
        private const int WM_CAP_SET_CALLBACK_VIDEOSTREAM   = WM_CAP_START + 6;
        private const int WM_CAP_SET_CALLBACK_WAVESTREAM    = WM_CAP_START + 7;
        private const int WM_CAP_GET_USER_DATA              = WM_CAP_START + 8;
        private const int WM_CAP_SET_USER_DATA              = WM_CAP_START + 9;
        private const int WM_CAP_DRIVER_CONNECT             = WM_CAP_START + 10;
        private const int WM_CAP_DRIVER_DISCONNECT          = WM_CAP_START + 11;
        private const int WM_CAP_DRIVER_GET_NAME            = WM_CAP_START + 12;//获得驱动程序名字
        private const int WM_CAP_DRIVER_GET_VERSION         = WM_CAP_START + 13;//获得驱动程序版本
        private const int WM_CAP_DRIVER_GET_CAPS            = WM_CAP_START + 14;//获得驱动程序帽子
        private const int WM_CAP_FILE_SET_CAPTURE_FILEA     = WM_CAP_START + 20;
        private const int WM_CAP_FILE_GET_CAPTURE_FILE      = WM_CAP_START + 21;//获得捕获文件
        private const int WM_CAP_FILE_ALLOCATE              = WM_CAP_START + 22;//分派文件
        private const int WM_CAP_FILE_SAVEAS                = WM_CAP_START + 23;//另存文件为
        private const int WM_CAP_FILE_SET_INFOCHUNK         = WM_CAP_START + 24;//设置开始文件
        private const int WM_CAP_FILE_SAVEDIB               = WM_CAP_START + 25;//保存文件
        private const int WM_CAP_EDIT_COPY                  = WM_CAP_START + 30;//编辑复制
        private const int WM_CAP_SET_AUDIOFORMAT            = WM_CAP_START + 35;//设置音频格式
        private const int WM_CAP_GET_AUDIOFORMAT            = WM_CAP_START + 36;//捕获音频格式
        private const int WM_CAP_DLG_VIDEOFORMAT            = WM_CAP_START + 41;//1065 打开视频格式设置对话框
        private const int WM_CAP_DLG_VIDEOSOURCE            = WM_CAP_START + 42;//1066 打开属性设置对话框，设置对比度亮度等
        private const int WM_CAP_DLG_VIDEODISPLAY           = WM_CAP_START + 43;//1067 打开视频显示
        private const int WM_CAP_GET_VIDEOFORMAT            = WM_CAP_START + 44;//1068 获得视频格式
        private const int WM_CAP_SET_VIDEOFORMAT            = WM_CAP_START + 45;
        private const int WM_CAP_DLG_VIDEOCOMPRESSION       = WM_CAP_START + 46;//1070 打开压缩设置对话框
        private const int WM_CAP_SET_PREVIEW                = WM_CAP_START + 50;
        private const int WM_CAP_SET_OVERLAY                = WM_CAP_START + 51;
        private const int WM_CAP_SET_PREVIEWRATE            = WM_CAP_START + 52;
        private const int WM_CAP_SET_SCALE                  = WM_CAP_START + 53;
        private const int WM_CAP_GET_STATUS                 = WM_CAP_START + 54;//获得状态
        private const int WM_CAP_SET_SCROLL                 = WM_CAP_START + 55;//设置卷
        private const int WM_CAP_GRAB_FRAME                 = WM_CAP_START + 60;
        private const int WM_CAP_SEQUENCE                   = WM_CAP_START + 62;
        private const int WM_CAP_SEQUENCE_NOFILE            = WM_CAP_START + 63;
        private const int WM_CAP_SET_SEQUENCE_SETUP         = WM_CAP_START + 64;//设置安装次序
        private const int WM_CAP_GET_SEQUENCE_SETUP         = WM_CAP_START + 65;//获得安装次序
        private const int WM_CAP_SET_MCI_DEVICE             = WM_CAP_START + 66;//设置媒体控制接口
        private const int WM_CAP_GET_MCI_DEVICE             = WM_CAP_START + 67;//获得媒体控制接口 
        private const int WM_CAP_STOP                       = WM_CAP_START + 68;
        private const int WM_CAP_ABORT                      = WM_CAP_START + 69;//异常中断
        private const int WM_CAP_SINGLE_FRAME_OPEN          = WM_CAP_START + 70;//打开单一的结构
        private const int WM_CAP_SINGLE_FRAME_CLOSE         = WM_CAP_START + 71;//关闭单一的结构
        private const int WM_CAP_SINGLE_FRAME               = WM_CAP_START + 72;//单一的结构
        private const int WM_CAP_PAL_OPEN                   = WM_CAP_START + 80;//打开视频
        private const int WM_CAP_PAL_SAVE                   = WM_CAP_START + 81;//保存视频
        private const int WM_CAP_PAL_PASTE                  = WM_CAP_START + 82;//粘贴视频
        private const int WM_CAP_PAL_AUTOCREATE             = WM_CAP_START + 83; //自动创造
        private const int WM_CAP_PAL_MANUALCREATE           = WM_CAP_START + 84;//手动创造
        private const int WM_CAP_SET_CALLBACK_CAPCONTROL    = WM_CAP_START + 85;// 设置收回的错误

        #endregion const

        public delegate void FrameEventHandler(IntPtr lwnd, IntPtr lpVHdr);

        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFOHEADER
        {
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biSize;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biWidth;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biHeight;
            [MarshalAs(UnmanagedType.I2)]
            public short biPlanes;
            [MarshalAs(UnmanagedType.I2)]
            public short biBitCount;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biCompression;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biSizeImage;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biXPelsPerMeter;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biYPelsPerMeter;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biClrUsed;
            [MarshalAs(UnmanagedType.I4)]
            public Int32 biClrImportant;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            [MarshalAs(UnmanagedType.Struct, SizeConst = 40)]
            public BITMAPINFOHEADER bmiHeader;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)]
            public Int32[] bmiColors;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct VideoHeader
        {
            [MarshalAs(UnmanagedType.I4)]
            public int lpData;
            [MarshalAs(UnmanagedType.I4)]
            public int dwBufferLength;
            [MarshalAs(UnmanagedType.I4)]
            public int dwBytesUsed;
            [MarshalAs(UnmanagedType.I4)]
            public int dwTimeCaptured;
            [MarshalAs(UnmanagedType.I4)]
            public int dwUser;
            [MarshalAs(UnmanagedType.I4)]
            public int dwFlags;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
            public int[] dwReserved;
        }
        /// <summary>包含控制视频流捕获过程的参数，如捕获帧频、指定键盘或鼠标键以终止捕获、捕获时间限制等；</summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct CAPTUREPARMS
        {
            /// <summary>期望的桢播放率，以毫秒为单位，默认为66667，相当于15桢每秒。</summary>
            public int dwRequestMicroSecPerFrame;
            /// <summary>Show "Hit OK to cap" dlg?开始捕获标志位，如果值为真，则在开始捕获前要产生一个询问对话框，默认为假。</summary>
            public bool fMakeUserHitOKToCapture;
            /// <summary>所允许的最大丢桢百分比，可以从0变化到100，默认值为10。</summary>
            public uint wPercentDropForError;
            /// <summary>另起线程标志位，如果为真，则程序重新启动一个线程用于视频流的捕获，默认值是假。但是如果你是为了真，你必须要在程序中处理一些潜在的操作，因为当视频捕获时，其他操作并没有被屏蔽。</summary>
            public bool fYield;
            /// <summary>在AVI文件中所允许的最大数目的索引项(32K)</summary>
            public int dwIndexSize;
            /// <summary>AVI文件的逻辑尺寸，以字节为单位。如果值是0，则说明该尺寸渐增 在 Win32程序中无用。(2K)</summary>
            public uint wChunkGranularity;
            /// <summary>Use DOS buffers?</summary>
            public bool fUsingDOSMemory;
            /// <summary>所被允许分配的最大视频缓存</summary>
            public uint wNumVideoRequested;
            /// <summary>音频标志位，如果音频流正在捕获，则该值为真。</summary>
            public bool fCaptureAudio;
            /// <summary>最大数量的音频缓存，默认值为10。</summary>
            public uint wNumAudioRequested;
            /// <summary>终止流捕获的虚拟键盘码，默认值为VK_ESCAPE</summary>
            public uint vKeyAbort;
            /// <summary>终止鼠标左键标志位，如果该值为真，则在流捕获过程中如果点击鼠标左键则该捕获终止，默认值为真。</summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool fAbortLeftMouse;
            /// <summary>Abort on right mouse?</summary>
            public bool fAbortRightMouse;
            /// <summary>捕获操作时间限制，如果为真，则时间到了以后捕获操作终止，默认为假</summary>
            public bool fLimitEnabled;
            /// <summary>具体终止时间，只有 fLimitEnabled是真时.该位才有效</summary>
            public uint wTimeLimit;
            /// <summary>Use MCI video source?</summary>
            public bool fMCIControl;
            /// <summary>Step MCI device?MCI 设备标志。</summary>
            public bool fStepMCIDevice;
            /// <summary>Time to start in MS</summary>
            public int dwMCIStartTime;
            /// <summary>Time to stop in MS</summary>
            public int dwMCIStopTime;
            /// <summary>Perform spatial averaging 2x</summary>
            public bool fStepCaptureAt2x;
            /// <summary>当基于平均采样来创建桢时，桢的采样时间，典型值是5</summary>
            public int wStepCaptureAverageFrames;
            /// <summary>音频缓存的尺寸，如果用默认值0，缓存尺寸是最大0.5秒，或10k字节。</summary>
            public int dwAudioBufferSize;
            /// <summary>Attempt to disable write cache</summary>
            public int fDisableWriteCache;
            /// <summary>音视频同步标志。</summary>
            public int AVStreamMaster;
        }
        /// <summary>定义了捕获驱动器的能力，如有无视频叠加能力、有无控制视频源、视频格式的对话框等； </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct CAPDRIVERCAPS
        {
            /// <summary>捕获驱动器的索引值，该值可以由0到9变化。</summary>
            [MarshalAs(UnmanagedType.U2)]
            public UInt16 wDeviceIndex;
            /// <summary>视频叠加标志，如果设备支持视频叠加这该位是真。</summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool fHasOverlay;
            /// <summary>视频资源对话框标志位，如果设备支持视频选择、控制对话框，该值为真。</summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool fHasDlgVideoSource;
            /// <summary>视频格式对话框标志位，如果设备支持对视频格式对话框的选择，该位真。</summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool fHasDlgVideoFormat;
            /// <summary>视频展示对话框标志位，如果设备支持对视频捕获缓存区的重新播放，该位是真。</summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool fHasDlgVideoDisplay;
            /// <summary>捕获安装标志位，如果捕获驱动器已经成功连接，该值为真。</summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool fCaptureInitialized;
            /// <summary>驱动器调色板标志位，如果驱动器能创建调色板，则该位是真。</summary>
            //[MarshalAs(UnmanagedType.Bool)]
            public bool fDriverSuppliesPalettes;
            [MarshalAs(UnmanagedType.I4)]
            public int hVideoIn;
            [MarshalAs(UnmanagedType.I4)]
            public int hVideoOut;
            [MarshalAs(UnmanagedType.I4)]
            public int hVideoExtIn;
            [MarshalAs(UnmanagedType.I4)]
            public int hVideoExtOut;
        }
        /// <summary>定义了捕获窗口的当前状态，如图像的宽、高等；</summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct CAPSTATUS
        {
            /// <summary>图像宽度</summary>
            public int uiImageWidth;
            /// <summary>图像高度</summary>
            public int uiImageHeight;
            /// <summary>活动窗口标记，如果窗口正以预览的方式展示图像，那么该值为真</summary>
            public bool fLiveWindow;
            /// <summary>叠加窗口标志位，如果正在使用硬件叠加，则该位是真。</summary>
            public bool fOverlayWindow;
            /// <summary>输入所放标志位，如果窗口是正在缩放视频到客户区，那么该位是真。当使用硬件叠加时，改位无效。</summary>
            public bool fScale;
            /// <summary>被展示在窗口客户区左上角的那个象素的x、y坐标偏移量。</summary>
            public Point ptScroll;
            /// <summary>默认调色板标志位，如果捕获窗口正在使用当前默认调色板，该值为真</summary>
            public bool fUsingDefaultPalette;
            /// <summary>音频硬件标志位，如果系统已经安装了音频硬件，该值为真。</summary>
            public bool fAudioHardware;
            /// <summary>捕获文件标志位，如果一个捕获文件已经被创建，该值为真</summary>
            public bool fCapFileExists;
            /// <summary>当前或最近流捕获过程中，所处理的桢的数目。包括丢弃的桢。</summary>
            public int dwCurrentVideoFrame;
            /// <summary>当前流捕获过程中丢弃的桢的数目。</summary>
            public int dwCurrentVideoFramesDropped;
            /// <summary># of wave samples cap'td</summary>
            public int dwCurrentWaveSamples;
            /// <summary>从当前流捕获开始计算，程序所用的时间，以毫秒为单位。</summary>
            public int dwCurrentTimeElapsedMS;
            /// <summary>当前剪切板的句柄。</summary>
            public IntPtr hPalCurrent;
            /// <summary>捕获标志位，当捕获是正在进行时，改位是真</summary>
            public bool fCapturingNow;
            /// <summary>错误返回值，当你的应用程序不支持错误回调函数时可以应用改位</summary>
            public int dwReturn;
            /// <summary>被分配的视频缓存的数目。</summary>
            public int wNumVideoAllocated;
            /// <summary>被分配的音频缓存的数目。</summary>
            public int wNumAudioAllocated;
        }

        [DllImport("avicap32.dll")]
        public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
        [DllImport("avicap32.dll")]
        public static extern bool capGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, int lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, int lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, FrameEventHandler lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref BITMAPINFO lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref CAPDRIVERCAPS lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref CAPTUREPARMS lParam);
        [DllImport("User32.dll")]
        public static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, ref CAPSTATUS lParam);

        [DllImport("User32.dll")]
        public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
        [DllImport("avicap32.dll")]
        public static extern int capGetVideoFormat(IntPtr hWnd, IntPtr psVideoFormat, int wSize);


        private static bool Connect          (IntPtr hwnd, short i)      { return SendMessage(hwnd, WM_CAP_DRIVER_CONNECT, i, 0); }
        private static bool Disconnect       (IntPtr hwnd)               { return SendMessage(hwnd, WM_CAP_DRIVER_DISCONNECT, 0, 0); }
        private static bool Preview          (IntPtr hwnd, bool f)       { return SendMessage(hwnd, WM_CAP_SET_PREVIEW, f, 0); }
        /// <summary>設定 Preview 模式下的更新頻率(毫秒)</summary>
        /// <param name="hwnd"></param>
        /// <param name="ms">更新頻率(毫秒)</param>
        /// <returns></returns>
        private static bool SetPreviewRate   (IntPtr hwnd, short ms)    { return SendMessage(hwnd, WM_CAP_SET_PREVIEWRATE, ms, 0); }
        private static bool SetCallbackOnFrame(IntPtr hwnd, FrameEventHandler callback)
        {
            return SendMessage(hwnd, WM_CAP_SET_CALLBACK_FRAME, 0, callback);
        }
        private static bool SetVideoFormat   (IntPtr hwnd, ref BITMAPINFO format, int size)
        {
            return SendMessage(hwnd, WM_CAP_SET_VIDEOFORMAT, size, ref format);
        }
        private static bool SetParameters    (IntPtr hwnd, ref CAPTUREPARMS parameters)
        {
            return SendMessage(hwnd, WM_CAP_SET_SEQUENCE_SETUP, SizeOf(parameters), ref parameters);
        }
        private static bool GetParameters    (IntPtr hwnd, ref CAPTUREPARMS parameters)
        {
            return SendMessage(hwnd, WM_CAP_GET_SEQUENCE_SETUP, SizeOf(parameters), ref parameters);
        }
        private static bool GetCapabilities  (IntPtr hwnd, ref CAPDRIVERCAPS capabilities)
        {
            return SendMessage(hwnd, WM_CAP_DRIVER_GET_CAPS, SizeOf(capabilities), ref capabilities);
        }
        private static bool GetStatus        (IntPtr hwnd, ref CAPSTATUS status)
        {
            return SendMessage(hwnd, WM_CAP_GET_STATUS, SizeOf(status), ref status);
        }
        private static bool SetScale(IntPtr hwnd, bool scaleOn)
        {
            return SendMessage(hwnd, WM_CAP_SET_SCALE, (short)(scaleOn ? 1 : 0), 0);
        }
        private static int SizeOf(ValueType structure) { return Marshal.SizeOf(structure); }
        private static int SizeOf<T>() { return Marshal.SizeOf(typeof (T)); }
        private static T PointerToStructure<T>(IntPtr ptr)
        {
            return (T)Marshal.PtrToStructure(ptr, typeof(T));
        }

        public event EventHandler<DataEventArgs<Bitmap>> BitmapReceived;
        protected virtual void OnBitmapReceived(DataEventArgs<Bitmap> e)
        {
            if (BitmapReceived != null) BitmapReceived(this, e);
        }
        public event EventHandler<VideoBufferReceivedEventArgs> VideoBufferReceived;
        protected virtual void OnVideoBufferReceived(VideoBufferReceivedEventArgs e)
        {
            if (VideoBufferReceived != null) VideoBufferReceived(this, e);
        }

        private readonly FrameEventHandler _frameCallback;
        private readonly byte[] _buffer;
        private IntPtr _device;
        private BITMAPINFO _bmpInfo;
        private CAPSTATUS _status;
        private CAPDRIVERCAPS _capabilities;

        public Foo()
        {
            _frameCallback = new FrameEventHandler(OnFrameCallback);
            _buffer = new byte[0xFFFFFF];
        }

        public void Start(short deviceIndex, IntPtr handle, int width, int height)
        {
            var name = new byte[100];
            var ver = new byte[100];
            capGetDriverDescriptionA(deviceIndex, name, 100, ver, 100);
            _device = capCreateCaptureWindowA(name, WS_VISIBLE | WS_CHILD, 0, 0, width, height, handle, 0);

            SetCallbackOnFrame(_device, _frameCallback);

            if (!Connect(_device, 0)) throw new Exception("Can not connect to device!");
            GetCapabilities(_device, ref _capabilities);
            GetStatus(_device, ref _status);

            SetPreviewRate(_device, 66);
            Preview(_device, true);
            SetScale(_device, true);
            var bmpInfo =
                new BITMAPINFO()
                    {
                        bmiHeader =
                            new BITMAPINFOHEADER()
                                {
                                    biSize = SizeOf<BITMAPINFOHEADER>(),
                                    biWidth = _status.uiImageWidth,
                                    biHeight = _status.uiImageHeight,
                                    biPlanes = 1,
                                    biBitCount = 24,
                                }
                    };
            SetVideoFormat(_device, ref bmpInfo, Marshal.SizeOf(typeof (BITMAPINFO)));
            _bmpInfo = bmpInfo;

            SetWindowPos(_device, 0, 0, 0, width, height, 6);



            //SendMessage(_device, WM_CAP_DLG_VIDEOFORMAT, 0, 0);
            //// 是否由新的图像尺寸？
            //// 如果有，发送通知给父窗口，告诉它尺寸改变了

            SendMessage(_device, WM_CAP_GET_STATUS, SizeOf(_status), ref _status);//获得当前视频流的尺寸 存入CapStatus结构

        }
        private void OnFrameCallback(IntPtr hwnd, IntPtr ptr)
        {
            var video = PointerToStructure<VideoHeader>(ptr);
            var data = new IntPtr(video.lpData);
            var len = video.dwBytesUsed;
            Marshal.Copy(data, _buffer, 0, len);

            //if (!File.Exists(@"C:\zzz"))
            //{
            //    using (var w = File.OpenWrite(@"C:\zzz"))
            //    {
            //        w.Write(_buffer, 0, len);
            //    }
            //}
            var rect = new Rectangle(0, 0, _bmpInfo.bmiHeader.biWidth, _bmpInfo.bmiHeader.biHeight);
            var bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format24bppRgb);
            var bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
            var scan0 = bmpData.Scan0;
            Marshal.Copy(_buffer, 0, scan0, len);
            bmp.UnlockBits(bmpData);
            //bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            OnBitmapReceived(new DataEventArgs<Bitmap>(bmp));
            //Marshal.Copy(data, _buffer, 0, video.dwBytesUsed);
            //OnVideoBufferReceived(new VideoBufferReceivedEventArgs(_buffer, video.dwBytesUsed));
        }
        public void Close()
        {
            Disconnect(_device);
        }
    }
}

